// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <cstddef>
#include <tuple>
#include <utility>

#include <deneb/internal/definitions.h>
#include <deneb/internal/information.h>
#include <deneb/internal/utility.h>

namespace deneb::internal {

    /// The information object for timed caches.
    ///
    /// TimedInformation differs from plain information only in that it stores the
    /// creation time, to know when a key has expired.
    ///
    /// \tparam Key The key type of the information.
    /// \tparam Value The value type of the information.
    template<typename Key, typename Value>
    struct TimedInformation : public Information<Key, Value> {
        using super = Information<Key, Value>;
        using typename super::QueueIterator;
        using Timestamp = internal::Timestamp;

        /// Constructor.
        ///
        /// \param value_ The value for the information.
        /// \param insertion_time_ The insertion timestamp of the key.
        /// \param order_ The order iterator for the information.
        TimedInformation(const Value &value_,
                         const Timestamp &insertion_time_,
                         QueueIterator order_ = QueueIterator())
                : super(value_, order_), insertion_time(insertion_time_) {
        }

        /// Constructor.
        ///
        /// Uses the current time as the insertion timestamp.
        ///
        /// \param value_ The value for the information.
        /// \param order_ The order iterator for the information.
        explicit TimedInformation(const Value &value_,
                                  QueueIterator order_ = QueueIterator())
                : TimedInformation(value_, internal::Clock::now(), order_) {
        }

        /// \copydoc Information::Information(QueueIterator,ValueArguments&&)
        template<typename... ValueArguments>
        TimedInformation(QueueIterator order_, ValueArguments &&... value_argument)
                : super(std::forward<ValueArguments>(value_argument)..., order_),
                  insertion_time(internal::Clock::now()) {
        }

        /// \copydoc Information::Information(QueueIterator,const
        /// std::tuple<ValueArguments...>&)
        template<typename... ValueArguments>
        explicit TimedInformation(
                const std::tuple<ValueArguments...> &value_arguments,
                QueueIterator order_ = QueueIterator())
                : super(value_arguments, order_), insertion_time(internal::Clock::now()) {
        }

        /// Compares this timed information for equality with another one.
        ///
        /// Additionally to key and value equality, the timed information requires
        /// that the insertion timestamps be equal.
        ///
        /// \param other The other timed information.
        /// \returns True if this information equals the other one, else false.
        bool operator==(const TimedInformation &other) const noexcept {
            if (super::operator!=(other)) return false;
            return this->insertion_time == other.insertion_time;
        }

        /// Compares this timed information for inequality with another one.
        ///
        /// \param other The other timed information.
        /// \returns True if this information does not equal the other one, else
        /// false.
        /// \see operator==()
        bool operator!=(const TimedInformation &other) const noexcept {
            return !(*this == other);
        }

        /// The time at which the key of the information was insterted into a cache.
        const Timestamp insertion_time;
    };

}  // namespace deneb::internal
